package singsong;

import java.util.LinkedList;
import java.util.Queue;

public class PeakToPeakGenerator {

	private final int samplesPerSecond;
	
	private final RecordedAudioClip buffer;
	
	private RecordedAudioClip clip = null;
	
	private Queue<AudioClip> completedClips = new LinkedList<AudioClip>();
	
	enum State { FIND_MIN, FIND_MAX, WAIT_PERIOD; }
	
	private State state = State.FIND_MIN;
	
	private float lastSample = Float.NaN;
	
	private float diffSinceBuffering = 0;
	
	private int sampleCount = 0;
	
	public PeakToPeakGenerator(int samplesPerSecond) {
		this.samplesPerSecond = samplesPerSecond;
		buffer = new RecordedAudioClip(samplesPerSecond);
	}
	
	public void addSample(float sample) {
		++sampleCount;
		if (Float.isNaN(lastSample)) {
			lastSample = sample;
		} else {
			if (state.equals(State.WAIT_PERIOD)) {
				lastSample = sample;
				clip.record(sample);
				if (clip.getDuration() > AudioConstants.MIN_PERIOD) {
					state = State.FIND_MIN;
				}
			} else {
				float sampleDiff = sample - lastSample;
				diffSinceBuffering += sampleDiff;
				lastSample = sample;
				if (state.equals(State.FIND_MIN)) {
					if (clip != null) {
						clip.record(sample);
					}
					if (diffSinceBuffering < 0) {
						diffSinceBuffering = 0;
					} else if (diffSinceBuffering > AudioConstants.MIN_AMP) {
						diffSinceBuffering = 0;
						state = State.FIND_MAX;
					}
				} else if (state.equals(State.FIND_MAX)) {
					if (diffSinceBuffering > 0) {
						flushBufferToClip();
						buffer.record(sample);
						diffSinceBuffering = 0;
					} else if (-diffSinceBuffering > AudioConstants.MIN_AMP) {
						completeClip();
						buffer.record(sample);
						flushBufferToClip();
						diffSinceBuffering = 0;
						state = State.WAIT_PERIOD;
						//System.out.printf("Peak start %f\n", ((float) sampleCount) / samplesPerSecond - clip.getDuration());
					} else {
						buffer.record(sample);
					}
				}
			}
		}
	}
	
	private void flushBufferToClip() {
		if (clip != null) {
			clip.append(buffer);
		}
		buffer.reset();
	}

	private void completeClip() {
		if (clip != null) {
			completedClips.add(clip);
		}
		clip = new RecordedAudioClip(samplesPerSecond);
	}

	public Queue<AudioClip> getAudioClipQueue() {
		return completedClips;
	}
}
